/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
 */

import { HosLocalComponentLoader } from '@ohos/hos-sdkmanager-common/build/src/hos/loader/hos-local-component-loader.js';
import { GlobalParam } from '@ohos/hvigor';
import { Component, OhLocalComponentLoader, PathAndApiVersion, SdkException, SdkProxyInfo, ApiVersion } from '@ohos/sdkmanager-common';
import fs from 'fs';
import path from 'path';

import { CommonConst } from '../const/common-const.js';
import { ProjectBuildProfile } from '../options/build/project-build-profile.js';
import { OhosLogger } from '../utils/log/ohos-logger.js';
import ApiMeta = ProjectBuildProfile.ApiMeta;
import ApiValType = ProjectBuildProfile.ApiValType;

const LOGGER: OhosLogger = OhosLogger.getLogger('Sdk');

/**
 * 根据OpenHarmony sdk目录结构规则预测是否存在相关组件
 * /${OhosSdkRoot}/${API}/${component}
 * /sdk/openharmony/10/toolchains
 *
 * @param sdkDir
 * @param version
 * @param components
 */
export const ohosPredict = (sdkDir: string, version: string, components: string[]) => {
  const result = new Map<PathAndApiVersion, Component>();

  const versionedDir: string = path.resolve(sdkDir, version);
  const lackedComponents: string[] = components.filter((cpn) => !fs.existsSync(path.resolve(versionedDir, cpn, CommonConst.OHOS_SDK_COMPONENT_DEFINITION)));
  if (lackedComponents.length > 0) {
    return result;
  }
  const parser: OhosParser = new OhosParser(sdkDir);
  const packages: string[] = components.map<string>((cpn) => path.resolve(versionedDir, cpn, 'oh-uni-package.json'));
  const componentDtos: Component[] = parser.parse(packages);
  const map: [PathAndApiVersion, Component][] = componentDtos.map((cpn) => [new PathAndApiVersion(cpn.getPath(), cpn.getFullApiVersion()), cpn]);
  map.forEach((tuple) => result.set(tuple[0], tuple[1]));
  return result;
};

/**
 * 复用sdk-manager的oh-uni-package.json的解析能力
 */
export class OhosParser extends OhLocalComponentLoader {
  constructor(sdkRoot: string) {
    super(sdkRoot);
  }

  parse(packages: string[]) {
    return this.parseSdks(packages);
  }
}

function getApiVersion(apiVersion: string, isHarmonyOS: boolean) {
  const errorId = isHarmonyOS ? 'HO_API_VERSION_VALIDATE_FAILED' : 'OH_API_VERSION_VALIDATE_FAILED';
  try {
    return new ApiVersion(apiVersion);
  } catch (e: any) {
    LOGGER.printErrorExit(errorId, undefined, [[path.resolve(process.cwd(), 'build-profile.json5')]]);
  }
  return undefined;
}

/**
 * 解析compileSdkVersion，compatibleSdkVersion，targetSdkVersion为ApiMeta对象
 *
 * @param sdkVersion 用户在build-profile里配置的compileSdkVersion，compatibleSdkVersion，targetSdkVersion
 * @param isHarmonyOS 判断是否为HarmonyOS项目
 */
export const parseApiVersion = (sdkVersion: string | number, isHarmonyOS: boolean) => {
  if (typeof sdkVersion === 'number') {
    return {
      api: `${sdkVersion}`,
      version: sdkVersion,
      type: ApiValType.NUM,
      fullVersion: sdkVersion.toString(),
      major: sdkVersion,
      minor: 0,
      patch: 0,
    } as ApiMeta;
  }

  // 先用老版本正则判断是否符合老版本的格式5.1.0(18)
  const reg = /\s*([1-9].[0-9].[0-9])\s*\(\s*(\d+)\s*\)\s*$/g;
  const regResult: RegExpExecArray | null = reg.exec(sdkVersion);
  if (regResult?.length === 3) {
    return {
      api: regResult![1],
      version: parseInt(regResult![2]),
      type: ApiValType.STRING,
      fullVersion: regResult![2],
      major: parseInt(regResult![2]),
      minor: 0,
      patch: 0,
    } as ApiMeta;
  }
  // 如果老版本格式未命中，则用新版本格式校验
  // 对于HarmonyOS-匹配出括号前和括号里面的部分
  // 对于OpenHarmony-匹配出整个部分
  if (isHarmonyOS) {
    const regFront = /\s*([1-9]\.[0-9]\.[0-9])\s*$/g;
    const IDEVersion: string = sdkVersion.split('(')[0];
    const apiVersion: string = sdkVersion.split('(')[1]?.split(')')[0];
    const IDEVersionRegResult: RegExpExecArray | null = regFront.exec(IDEVersion);
    if (IDEVersionRegResult?.length !== 2) {
      LOGGER.printErrorExit('HO_API_VERSION_VALIDATE_FAILED', undefined, [[path.resolve(process.cwd(), 'build-profile.json5')]]);
    }
    const fullApiVersion:ApiVersion | undefined = getApiVersion(apiVersion, true);
    return {
      api: IDEVersion,
      version: fullApiVersion?.getMajor(),
      type: ApiValType.STRING,
      fullVersion: apiVersion,
      major: fullApiVersion?.getMajor(),
      minor: fullApiVersion?.getMinor(),
      patch: fullApiVersion?.getPatch(),
    } as ApiMeta;
  } else {
    const fullApiVersion: ApiVersion | undefined = getApiVersion(sdkVersion, false);
    return {
      api: sdkVersion,
      version: fullApiVersion?.getMajor(),
      type: ApiValType.STRING,
      fullVersion: sdkVersion,
      major: fullApiVersion?.getMajor(),
      minor: fullApiVersion?.getMinor(),
      patch: fullApiVersion?.getPatch(),
    } as ApiMeta;
  }
};

export const proxyFun = (): SdkProxyInfo => {
  const globalParam: GlobalParam = GlobalParam.getInstance();
  return {
    proxy: globalParam.getConfig('proxy'),
    httpsProxy: globalParam.getConfig('https-proxy'),
    noProxy: globalParam.getConfig('noproxy') ?? globalParam.getConfig('no-proxy') ?? process.env['NO_PROXY'] ?? process.env['npm_config_noproxy'],
  };
};

export const contains = (pathAndApi: string, all: Map<string, Component>) => {
  for (const [key, component] of all) {
    if (key === pathAndApi) {
      return component;
    }
  }
  return undefined;
};

export const handleSdkException = (ex: any) => {
  if (ex instanceof SdkException) {
    const sdkException: SdkException = ex as SdkException;
    const errTip = sdkException.getErrorTip();
    LOGGER._buildError(errTip.getReason())._solution(`${errTip.getFixSuggestion()}`)._printErrorAndExit();
  } else {
    throw ex;
  }
};
